import db from '@/db'
import utils from '@utils/utils'
import userService from '@/service/userService'

/**
 * 用于操作conversation表的service
 */
export default {
  /**
   * 从api获取会话列表
   * @param params
   */
  fetchApiPage(params) {
    return new Promise((resolve, reject) => {
      // 拿到chatGptToken后，去Get请求对话列表
      chrome.storage.local.get(['chatGptToken'], result => {
        // 创建一个URL对象
        const url = new URL('https://chat.openai.com/backend-api/conversations')

        // 创建URLSearchParams对象，并添加查询参数
        url.search = new URLSearchParams({
          offset: params.offset ?? 0,
          limit: params.limit ?? 250,
          order: 'updated',
        })

        fetch(url, {
          method: 'GET',
          headers: {
            Authorization: result.chatGptToken,
          },
        })
          .then(res => {
            // 如果不是200，reject
            if (!res.ok) {
              reject(new Error(JSON.stringify({
                success: false,
                message: 'Http code is not 200',
              })))
            } else {
              resolve(res.json())
            }
          })
          .catch(error => {
            reject(error)
          })
      })
    })
  },
  async fetchApiAll() {
    const page = {
      limit: 100, // 每页数量
      offset: 0, // 偏移量
      total: null, // 总数
    }

    const apiData = []

    // 分页获取对话列表
    let moreDataExists = true
    while (moreDataExists) {
      // eslint-disable-next-line no-await-in-loop
      const response = await this.fetchApiPage(page)
        .catch(error => {
          throw error
        })
      const apiPageData = response.items || []
      if (!apiPageData.length) {
        break
      }

      // 有数据，处理数据
      apiData.push(...apiPageData)

      // 存储第一次获取到的总数，避免后续发生变化
      if (page.total === null) {
        page.total = response.total
      }

      page.offset += page.limit
      if (apiData.length >= page.total) {
        // 最后一页
        moreDataExists = false
        break
      }

      // 模拟网络延迟
      // eslint-disable-next-line no-await-in-loop
      await utils.delay()
    }

    return {
      data: apiData,
      success: true,
    }
  },

  /**
   * 批量添加conversation（到默认文件夹）
   * @param conversations
   */
  async addBatch(conversations) {
    const conversationList = [...conversations]
    const user = await userService.getCurrent()
    const listCopy = []

    // 遍历conversationList，设置folderId为1
    conversationList.forEach(item => {
      listCopy.push({
        ...item,
        folderId: 1,
        userId: user.id,
        order: 0,
        syncVersion: 0,
      })
    })

    return db.conversation.bulkAdd(listCopy)
  },

  // /**
  //  * 移动会话目录
  //  */
  // moveFolder(id, folderId) {
  //   return db.conversation.update(id, {
  //     folderId,
  //     order: null,
  //   })
  // },

  /**
   * 移动到默认目录
   * @param folderId
   */
  moveFolderToDefault(folderId) {
    return db.conversation.where('folderId').equals(folderId).modify({ folderId: 1, order: null })
  },

  /**
   * 移动会话顺序
   * @param id
   * @param folderId
   * @param newIndex
   */
  async move(id, folderId, newIndex) {
    // 根据id获取会话
    const conversation = await db.conversation.get(id)

    const updateObj = {
      order: newIndex ?? 0,
    }

    const user = await userService.getCurrent()

    // 获取目录下的所有会话，然后排序
    const conversations = await db.conversation
      .where({
        folderId,
        userId: user.id,
      })
      .toArray()
    const sorted = this.sorted(conversations)

    // 判断是否更改了目录, 如果更改了目录，需要将order置为null
    if (conversation.folderId !== folderId) {
      // 此时conversationCopy的order应该为新目录下的会话数量
      updateObj.folderId = folderId
      updateObj.order = newIndex

      // 将这个对话插入到sorted中
      sorted.splice(newIndex, 0, conversation)

      // 原来的目录下的会话，order需要减1
      // 但考虑性能问题，似乎不减1也没什么问题
    }

    const isPinLength = sorted.filter(item => item.isPin).length

    // 如果小于isPinLength，且本身不是置顶会话，那么设置为置顶会话
    // 如果大于等于isPinLength, 并且本身是置顶的, 那么就设置为不置顶
    if (newIndex < isPinLength && !conversation.isPin) {
      updateObj.isPin = true
    } else if (newIndex >= isPinLength && conversation.isPin) {
      updateObj.isPin = false
    }

    // 更新conversation
    db.conversation.update(id, { ...updateObj })

    // 处理会话顺序
    // 获取当前会话的索引
    const currentIndex = sorted.findIndex(item => item.id === id)
    const currentConversation = sorted.splice(currentIndex, 1)[0]
    sorted.splice(newIndex, 0, currentConversation)

    // 获取currentIndex和newIndex之间的会话
    let between = []
    if (currentIndex <= newIndex) {
      between = sorted.slice(currentIndex, newIndex)
    } else {
      between = sorted.slice(newIndex + 1, currentIndex + 1)
    }

    // 遍历between，更新order，这样只需要处理变化的order
    between.forEach((item, index) => {
      // 如果是向上移动，从currentIndex开始，item的order需要加1
      // 如果是向下移动, 从newIndex开始，item的order需要减1
      const order = currentIndex < newIndex ? index + currentIndex : index + 1 + newIndex

      db.conversation.update(item.id, {
        order,
      })
    })
  },

  /**
   * 排序
   * @param conversations
   */
  sorted(conversations) {
    const arr = [...conversations]

    // 按照排序规则排序
    // 1. 取出置顶会话，按照order排序
    // 2. 取出非置顶会话，没有order的，按照update_time排序
    // 3. 取出非置顶会话，有order的，按照order排序
    const pinConversations = arr.filter(item => item.isPin).sort((a, b) => a.order - b.order) // 1
    const noPinConversations = arr.filter(item => !item.isPin) // 2
    const noPinConversationsWithoutOrder = noPinConversations
      .filter(item => !item.order)
      .sort((a, b) => {
        const timeStampA = new Date(a.update_time).getTime() / 1000
        const timeStampB = new Date(b.update_time).getTime() / 1000

        // 时间戳大的排在前面
        return timeStampB - timeStampA
      }) // 2
    const noPinConversationsWithOrder = noPinConversations
      .filter(item => item.order)
      .sort((a, b) => a.order - b.order) // 3

    const result = [...pinConversations, ...noPinConversationsWithoutOrder, ...noPinConversationsWithOrder]

    return result

    //
    // return arr.sort((a, b) => {
    //   if (a.isPin && !b.isPin) {
    //     return -1
    //   }
    //   if (!a.isPin && b.isPin) {
    //     return 1
    //   }
    //
    //   return a.order - b.order
    // })
  },

  /**
   * 删除会话
   */
  remove(id) {
    return new Promise((resolve, reject) => {
      // 拿到chatGptToken后，去Get请求对话列表
      chrome.storage.local.get(['chatGptToken'], result => {
        // fetch删除会话
        fetch(`https://chat.openai.com/backend-api/conversation/${id}`, {
          method: 'PATCH',
          headers: {
            Authorization: result.chatGptToken,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ is_visible: false }),
        }).then(res => {
          // 如果不是200，reject
          if (!res.ok) {
            reject(new Error('Uh-oh! Refresh or re-login ChatGPT. 🔄'))
          } else {
            // 从数据库中删除
            db.conversation.delete(id)
            resolve(res.json())
          }
        })
          .catch(error => {
            reject(error)
          })
      })
    })
  },
  delete(id) {
    return db.conversation.delete(id)
  },

  /**
   * 更新会话标题
   * @param id
   * @param title
   */
  updateTitle(id, title) {
    // 检查是否需要更新
    const conversation = db.conversation.get(id)
    if (!conversation || conversation.title === title || !title) {
      return Promise.resolve()
    }

    return new Promise((resolve, reject) => {
      // 拿到chatGptToken后，去Get请求对话列表
      chrome.storage.local.get(['chatGptToken'], result => {
        // fetch修改
        fetch(`https://chat.openai.com/backend-api/conversation/${id}`, {
          method: 'PATCH',
          headers: {
            Authorization: result.chatGptToken,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ title }),
        }).then(res => {
          // 如果不是200，reject
          if (!res.ok) {
            reject(new Error('Uh-oh! Refresh or re-login ChatGPT. 🔄'))
          } else {
            // 修改数据库
            db.conversation.update(id, { title })
            resolve(res.json())
          }
        })
          .catch(error => {
            reject(error)
          })
      })
    })
  },

  /**
   * 从api获取会话详情
   * @param id
   */
  fetchApiDetail(id) {
    if (!id) return Promise.resolve()

    return new Promise((resolve, reject) => {
      chrome.storage.local.get(['chatGptToken'], result => {
        // fetch获取会话
        fetch(`https://chat.openai.com/backend-api/conversation/${id}`, {
          method: 'GET',
          headers: {
            Authorization: result.chatGptToken,
            'Content-Type': 'application/json',
          },
        }).then(res => {
          // 如果不是200，reject
          if (!res.ok) {
            reject(new Error('Uh-oh! Refresh or re-login ChatGPT. 🔄'))
          } else {
            resolve(res.json())
          }
        })
          .catch(error => {
            reject(error)
          })
      })
    })
  },

  /**
   * 保存或更新会话
   * @param id
   * @param detail
   */
  async updateMapping(id, detail) {
    // 更新会话的最后更新时间，最终确认会话更新完成
    await db.conversation.update(id, {
      syncVersion: detail.update_time,
      mapping: detail.mapping,
      current_node: detail.current_node,
    })

    return Promise.resolve()
  },

  /**
   * 根据过滤器查找会话
   * @param filter
   * @returns {Promise<*[]>}
   */
  async getListByFilter(filter = {}) {
    if (!filter.search?.trim()) {
      return []
    }

    const findList = [
      // {
      //   title: '...',
      //   id: '...',
      //   updated_time: '...',
      //   matchItems: [
      //     {
      //       id: '...', // titleIsNull
      //       field: 'title/message',
      //     }
      //   ]
      // }
    ]
    const user = await userService.getCurrent()
    const list = await db.conversation.where({
      userId: user.id,
    }).toArray() || []

    const searchText = (filter.search || '').trim()
    const regx = new RegExp(searchText, 'gi')

    list.forEach(item => {
      const findItem = {
        title: item.title,
        id: item.id,
        update_time: item.update_time,
        matchResults: 0,
      }

      // 查找title中是否包含, 不区分大小写
      findItem.matchResults += (item.title.match(regx) || []).length

      // 拿到author不为system的mapping
      const mapping = item.mapping || {}
      if (!mapping) {
        return
      }
      let filterMapping = {}

      const currentNodeId = item.current_node
      if (currentNodeId) {
        let currentNode = mapping[currentNodeId]
        while (currentNode) {
          if (currentNode.message && currentNode.message.author.role !== 'system') {
            filterMapping[currentNode.id] = currentNode
          }
          currentNode = mapping[currentNode.parent]
        }
      } else {
        // 历史数据兼容
        filterMapping = { ...mapping }
      }

      // 遍历mapping中的每一个key
      // 如果有message.content.parts, 就取第0个，判断是否包含，不区分大小写
      Object.keys(filterMapping).forEach(key => {
        const value = filterMapping[key]
        if (value?.message?.content?.parts) {
          const message = value.message.content.parts[0]
          findItem.matchResults += (message?.match(regx) || []).length
        }
      })
      if (findItem.matchResults > 0) {
        findList.push(findItem)
      }
    })

    // 最后按照matchItems的长度排序
    return findList.sort((a, b) => b.matchResults - a.matchResults)
  },

  /**
   * 增量更新conversation
   */
  async incrementalUpdateList() {
    const page = {
      offset: 0, // 偏移量
      limit: 100, // 每页数量
      total: null, // 总数
    }

    // 从db获取conversation列表，按update_time倒序
    const localData = await db.conversation.orderBy('update_time').reverse().toArray() || []

    // 存储从API获取的所有数据
    const apiData = []

    // 分页获取对话列表
    let loadMore = true
    const updateQueue = []

    while (loadMore) {
      // eslint-disable-next-line no-await-in-loop
      const result = await this.fetchApiPage(page)
        .catch(error => {
          throw error
        })

      const apiPageData = result.items || []

      // 有数据，处理数据
      apiData.push(...apiPageData)

      // 存储第一次获取到的总数，避免后续发生变化
      if (page.total === null) {
        page.total = result.total
      }

      apiPageData.forEach(apiItem => {
        const updateTime = new Date(apiItem.update_time)
        const updateTimeStamp = updateTime.getTime() / 1000
        const localItem = localData.find(item => item.id === apiItem.id)
        if (!localItem) {
          // 如果在API中存在，但在本地不存在的条目
          this.addBatch([apiItem])
          updateQueue.push(apiItem.id)
        } else if (localItem && localItem.syncVersion !== updateTimeStamp) {
          // 如果在API和本地中都存在，但update_time不一致的条目
          db.conversation.update(localItem.id, {
            title: apiItem.title,
            update_time: apiItem.update_time,
          })

          updateQueue.push(apiItem.id)
        }
      })

      // 当前页最后一条数据在本地没有变化，说明没有更多的新数据需要更新
      const lastItemInPage = apiPageData[apiPageData.length - 1]
      const lastItemInLocalIndex = localData.findIndex(item => item.id === lastItemInPage.id)

      const lastItemUpdateTimeStamp = new Date(lastItemInPage.update_time).getTime() / 1000

      if (lastItemInLocalIndex >= 0
        && localData[lastItemInLocalIndex].syncVersion === lastItemUpdateTimeStamp) {
        const localRemainingItems = localData.length - lastItemInLocalIndex
        const apiRemainingItems = (page.offset + page.limit) - apiData.length

        // 如果剩余的数据条数相等，则退出循环
        if (localRemainingItems === apiRemainingItems) {
          loadMore = false
          break
        }
      }

      page.offset += page.limit
      if (apiData.length >= page.total) {
        // 最后一页
        loadMore = false
        break
      }

      // 模拟网络延迟
      // eslint-disable-next-line no-await-in-loop
      await utils.delay()
    }

    // 处理在本地存在，但在API中不存在的条目
    localData.forEach(localItem => {
      const apiItem = apiData.find(item => item.id === localItem.id)
      if (!apiItem) {
        db.conversation.delete(localItem.id)
      }
    })

    return {
      conversationIds: updateQueue,
    }
  },

  // async rebuildCache() {
  //   const list = await db.conversation.toArray() || []
  //   for (let i = 0; i < list.length; i += 1) {
  //     const item = list[i]
  //     await db.conversation.update(item.id, {
  //       update_time: 0,
  //       syncVersion: 0,
  //     })
  //   }
  //   if (list.length) {
  //     this.incrementalUpdateList()
  //   }
  // },

}
